package com.basker.pisces.orm.database;

import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import com.basker.pisces.concurrent.lock.ILock;
import com.basker.pisces.concurrent.lock.ILockManager;
import com.basker.pisces.config.ApplicationConfig;
import com.basker.pisces.core.datasource.IDataSourceManager;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.annotation.DataObjectMeta;
import com.basker.pisces.core.meta.bean.BeanMetaManager;
import com.basker.pisces.core.meta.bean.BeanMetaTypeWrapperRegistrar.BeanMetaTypeWrapper;
import com.basker.pisces.orm.data.meta.DataSetMeta;
import com.basker.pisces.spring.SpringApplicationContext;

/**
 * 获取所有BeanMeta,将其映射的数据表结构同步到数据库
 *
 * @author hangwen
 * @see EnableBeanMetaDatabaseSynchronizer
 */
public class BeanMetaDatabaseSynchronizer implements InitializingBean {

    @Autowired(required = false)
    private List<BeanMetaTypeWrapper> beanMetaTypeWrappers;

    @Autowired
    private ApplicationConfig appConfig;

    @Autowired
    private IDataSourceManager dataSourceManager;

    private Set<String> excludeSchemas;

    @Override
    public void afterPropertiesSet() throws Exception {
        this.setExcludeSchemas();
        this.trySynDatabase();
    }

    private void setExcludeSchemas() {
        String[] array = this.appConfig.getStringArray("pisces.orm.db.syn.exclude", ",");
        if (ArrayUtils.isEmpty(array)) {
            return;
        }

        excludeSchemas = new HashSet<>(Arrays.asList(array));
    }

    private void trySynDatabase() {
        if (CollectionUtils.isEmpty(this.beanMetaTypeWrappers)) {
            return;
        }

        ILockManager lockManager = SpringApplicationContext.getInstance().getBean(ILockManager.class);
        ILock lock = lockManager.getLock("syndb");

        if (!lock.lock()) {
            return;
        }

        try {
            for (BeanMetaTypeWrapper dataObjectMetaTypeWrapper : beanMetaTypeWrappers) {
                Class<?> beanType = dataObjectMetaTypeWrapper.getBeanType();
                DataObjectMeta annotation = beanType.getAnnotation(DataObjectMeta.class);

                if (StringUtils.isEmpty(annotation.tableName())) {
                    continue;
                }

                IDataObjectMeta objectMeta = BeanMetaManager.getBeanMeta(beanType);
                if (this.excludeSchemas != null) {
                    String schema = objectMeta.getSchema();
                    if (StringUtils.isEmpty(schema)) {
                        schema = dataSourceManager.getDefaultDataSourceName();
                    }

                    if (this.excludeSchemas.contains(schema)) {
                        continue;
                    }
                }

                DataSetMeta dataSetMeta = DataSetMeta.create(objectMeta);

                DatabaseDefinitionSynchronizer databaseDefinitionSynchronizer = new DatabaseDefinitionSynchronizer(
                        dataSetMeta);
                databaseDefinitionSynchronizer.syn();
            }
        } finally {
            lock.unlock();
        }
    }

}
